// This is the main DLL file.

#include "stdafx.h"

#include "m4aFile.h"
#include "AtomicParsley.h"
#include "AP_commons.h"
#include "AP_AtomExtracts.h"

using namespace System::IO;
using namespace System::Text;
using namespace System::Diagnostics;
using namespace System::Runtime::InteropServices;
using namespace System::Collections::Generic;

using namespace m4aSharp;

m4aFile::m4aFile(String^ path) 
{
	Debug::Assert(String::IsNullOrEmpty(path) == false);
			
	_path = path;
	_atoms = gcnew Dictionary<String^, Atom^>(StringComparer::OrdinalIgnoreCase);
}		

Atom^ m4aFile::GetAtom(String^ atomPath)
{
	Debug::Assert(String::IsNullOrEmpty(atomPath) == false);

    Queue<String^>^ parts = gcnew Queue<String^>(atomPath->Split('.'));

    Atom^ a = nullptr;
			
	if (Atoms->ContainsKey(parts->Peek()))
	{
		a = Atoms[parts->Dequeue()];

		while (parts->Count > 0 && a != nullptr)
		{
			String^ part = parts->Dequeue();
					
			for each (Atom^ atom in a->Atoms)
			{
				if (atom->Name->Equals(part, StringComparison::OrdinalIgnoreCase))
				{
					a = atom;
					break;
				}
				else
				{
					a = nullptr;
				}
			}          
		}
	}
	return a;
}

void m4aFile::Load()
{
	if (!File::Exists(_path))
		throw gcnew FileNotFoundException("M4A file not found", _path);		

	AtomicParsley parsley;
			 
	// load the atoms from the file
	IntPtr m4afile = Marshal::StringToHGlobalAnsi(_path);
	if (parsley.APar_ScanAtoms((const char*)m4afile.ToPointer(), true))
	{
		parsley.openSomeFile((const char*)m4afile.ToPointer(), true);		

		// get the toal number of tracks in this file
		uint8_t track = 0;
		uint8_t total = 0;
		short codec = 0;
		parsley.APar_TrackInfo(total, track, codec);

		_totalTracks = total;

		for (int iter = 0; iter < parsley.atom_number; iter++)
		{
			AtomicInfo* info = parsley.GetParsedAtom(iter);
			if(info->AtomicLevel == 1)
			{
				Atom^ atom = CreateAtom(parsley, info);
				_atoms->Add(atom->Name, atom);		
			}
		}

		Extract e(&parsley);
		TrackInfo* t = e.CreateTrackInfo();
		_trackData = gcnew TrackData(t);
		free(t);
	}

	Marshal::FreeHGlobal(m4afile);
}

Atom^ m4aFile::CreateAtom(AtomicParsley& parsley, AtomicInfo* info)
{
	Atom^ atom = gcnew Atom(gcnew String(info->AtomicName));

	// recursivly create child Atoms from the list
	if (info->NextAtomNumber > 0) // NextAtomNumber == 0 means end of the list
	{
		// iterate from the current position to the end
		// stop if we find an atom which is at the current level
		for (int iter = info->NextAtomNumber; iter < parsley.atom_number; iter++)
		{
			AtomicInfo* next = parsley.GetParsedAtom(iter);
			if (next->AtomicLevel == info->AtomicLevel + 1)
				atom->AddChildAtom(CreateAtom(parsley, next));	

			else if (next->AtomicLevel == info->AtomicLevel)
				break;
		}
	}

	// for leaf nodes - copy the data to the managed heap	
	if (atom->Atoms->Count == 0 && atom->Name != "mdat") // don't want to load the media stream into memory
		parsley.APar_AtomicRead(info->AtomicNumber); // get the data payload

	if (info->AtomicData != NULL)
	{				
		// copy the AtomicInfo data payload onto the managed heap
		array<System::Byte>^ bytes = gcnew array<System::Byte>(info->AtomicLength);
		Marshal::Copy((IntPtr)info->AtomicData, bytes, 0, info->AtomicLength);
		atom->SetData(bytes);
	}

	return atom;
}

